﻿using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Entities;
using Abp.Linq.Extensions;
using Hicap.QRCodeWarehouse.Application.Dto.Common;
using Hicap.QRCodeWarehouse.Application.Helper;
using Hicap.QRCodeWarehouse.Core.Identity;
using Hicap.QRCodeWarehouse.Core.Managers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Hicap.QRCodeWarehouse.Application
{
    /// <summary>
    /// Service base
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TEntityListDto"></typeparam>
    /// <typeparam name="TEntityEditDto"></typeparam>
    [HicapAuthorize]
    public abstract class QrCodeWarehouseApplicationBase<TEntity, TEntityListDto, TEntityEditDto> : IQrCodeWarehouseApplicationBase<TEntityListDto, TEntityEditDto> where TEntity : class, IEntity, ISoftDelete
        where TEntityListDto : class, IEntityDto
        where TEntityEditDto : class, IEntityDto<int?>
    {
        private readonly IManagerBase<TEntity> _manager;

        protected QrCodeWarehouseApplicationBase(IManagerBase<TEntity> manager) : base()
        {
            _manager = manager;
        }

        /// <summary>
        /// Convert QueryInput to IQueryable&lt;TEntity&gt;
        /// </summary>
        /// <param name="query">IQueryable&lt;TEntity&gt;</param>
        /// <param name="queryInput">QueryInput</param>
        /// <returns>IQueryable&lt;TEntity&gt;</returns>
        protected IQueryable<TEntity> GetQuery(IQueryable<TEntity> query, QueryInput queryInput = null)
        {
            return GetQueryBuilder(query, queryInput);
        }

        /// <summary>
        /// Convert QueryInput to IQueryable&lt;TEntity&gt;
        /// </summary>
        /// <param name="query">IQueryable&lt;TEntity&gt;</param>
        /// <param name="queryInput">QueryInput</param>
        /// <returns>IQueryable&lt;TEntity&gt;</returns>
        private IQueryable<TEntity> GetQueryBuilder(IQueryable<TEntity> query, QueryInput queryInput = null)
        {
            //make  expression tree by queryInput
            System.Linq.Expressions.Expression<System.Func<TEntity, bool>> filter = queryInput != null ?
                ExpressionHelper<TEntity>.CreateExpression(queryInput?.Filter) : null;

            IQueryable<TEntity> queryable = filter != null ? query.Where(filter) : query;
            return queryable.AsNoTracking();
        }

        /// <summary>
        /// Query with paged
        /// </summary>
        /// <param name="queryInput">QueryInput</param>
        /// <returns>PagedResultDto&lt;TEntityListDto&gt;</returns>
        public virtual async Task<PagedResultDto<TEntityListDto>> PagedDatas(QueryInput queryInput)
        {
            IQueryable<TEntity> query = GetQuery(_manager.GetQuery(null), queryInput);

            int count = query.Count();

            List<TEntity> result = await query
                .OrderBy(queryInput.Sorting)
                .PageBy(queryInput)
                .ToListAsync();

            List<TEntityListDto> resultListDto = result.MapTo<List<TEntityListDto>>();

            return new PagedResultDto<TEntityListDto>(
                count,
                resultListDto
            );
        }

        /// <summary>
        /// Query single by id
        /// </summary>
        /// <param name="input">int</param>
        /// <returns>TEntityListDto</returns>
        public virtual async Task<TEntityListDto> GetById(int input)
        {
            TEntity dbEntity = await _manager.GetQuery(null).FirstOrDefaultAsync(x => x.Id == input);
            TEntityListDto entity = dbEntity.MapTo<TEntityListDto>();
            return entity;
        }

        /// <summary>
        /// CheckHealth
        /// </summary>
        /// <returns>OK/Fail</returns>
        [HttpGet]
        [AllowAnonymous]
        public virtual async Task<IActionResult> CheckHealth()
        {
            if (await _manager.CheckHealth())
            {
                return new OkResult();
            }
            else
            {
                return new StatusCodeResult(500);
            }
        }

        /// <summary>
        /// Upsert Data
        /// </summary>
        /// <param name="input">CreateOrUpdateInput&lt;TEntityEditDto&gt;</param>
        /// <returns>int</returns>
        public virtual async Task<int> Upsert(CreateOrUpdateInput<TEntityEditDto> input)
        {
            TEntity entity = input.Entity.MapTo<TEntity>();
            return await _manager.InsertOrUpdate(entity);
        }

        /// <summary>
        /// Batch Upsert Data
        /// </summary>
        /// <param name="inputs">List&lt;CreateOrUpdateInput&lt;TEntityEditDto&gt;&gt;</param>
        /// <returns>int</returns>
        public virtual async Task<int> BatchUpsert(List<CreateOrUpdateInput<TEntityEditDto>> inputs)
        {
            List<TEntity> list = new List<TEntity>();
            inputs.ForEach(x => list.Add(x.Entity.MapTo<TEntity>()));
            return await _manager.BatchInsertOrUpdate(list);
        }

        /// <summary>
        /// Delete data by id
        /// </summary>
        /// <param name="input">int</param>
        /// <returns></returns>
        [NonAction]
        public virtual async Task<int> Delete(int input)
        {
            return await _manager.Delete(input);
        }

        /// <summary>
        /// Batch delete data by id
        /// </summary>
        /// <param name="input">List&lt;int&gt; </param>
        /// <returns>int</returns>
        public virtual async Task<int> BatchDelete(List<int> input)
        {
            return await _manager.BatchDelete(input);
        }
    }
}